package com.qxp.simplerpc.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@SuppressWarnings("unchecked")
@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate redisTemplate;

    private Lock lock = new ReentrantLock();

    public <T> T get(String key) {
        T result = null;
        try {
            result = (T) this.redisTemplate.opsForValue().get(key);
        } finally {
            releaseConnection();
        }
        return result;
    }

    public void set(String key, Object val) {
        try {
            this.redisTemplate.opsForValue().set(key, val);
        } finally {
            releaseConnection();
        }
    }

    public void set(String key, Object val, long l, TimeUnit t) {
        try {
            this.redisTemplate.opsForValue().set(key, val, l, t);
        } finally {
            releaseConnection();
        }
    }

    public void addMap2Value(String key, String k, Object v, long l, TimeUnit t) {
        try {
            lock.lock();
            Map<String, Object> map = (Map<String, Object>) this.redisTemplate.opsForValue().get(key);
            if (Objects.isNull(map)) {
                map = new HashMap<>();
            }
            map.put(k, v);
            this.redisTemplate.opsForValue().set(key, map, l, t);
        } finally {
            releaseConnection();
            lock.unlock();
        }
    }

    public void addMap2Value(String key, String k, Object v) {
        try {
            lock.lock();
            Map<String, Object> map = (Map<String, Object>) this.redisTemplate.opsForValue().get(key);
            if (Objects.isNull(map)) {
                map = new HashMap<>();
            }
            map.put(k, v);
            this.redisTemplate.opsForValue().set(key, map);
        } finally {
            releaseConnection();
            lock.unlock();
        }
    }

    public void addAllMap2Value(String key, Map<String, Object> v) {
        try {
            lock.lock();
            Map<String, Object> map = (Map<String, Object>) this.redisTemplate.opsForValue().get(key);
            if (Objects.isNull(map)) {
                map = new HashMap<>();
            }
            map.putAll(v);
            this.redisTemplate.opsForValue().set(key, map);
        } finally {
            releaseConnection();
            lock.unlock();
        }
    }

    public void removeMap4Value(String key, String k) {
        try {
            lock.lock();
            Map<String, Object> map = (Map<String, Object>) this.redisTemplate.opsForValue().get(key);
            if (!CollectionUtils.isEmpty(map)) {
                map.remove(k);
            }
            this.redisTemplate.opsForValue().set(key, map);
        } finally {
            releaseConnection();
            lock.unlock();
        }
    }

    public void removeMap4Value(String key, String k, long l, TimeUnit t) {
        try {
            lock.lock();
            Map<String, Object> map = (Map<String, Object>) this.redisTemplate.opsForValue().get(key);
            if (!CollectionUtils.isEmpty(map)) {
                map.remove(k);
            }
            this.redisTemplate.opsForValue().set(key, map, l, t);
        } finally {
            releaseConnection();
            lock.unlock();
        }
    }

    public void removeAllMap2Value(String key, Map<String, Object> v) {
        try {
            lock.lock();
            Map<String, Object> map = (Map<String, Object>) this.redisTemplate.opsForValue().get(key);
            if (!CollectionUtils.isEmpty(map)) {
                v.forEach((k, vl) -> {
                    map.remove(k);
                });
            }
            this.redisTemplate.opsForValue().set(key, map);
        } finally {
            lock.unlock();
        }
    }

    public void removeAllMap2Value(String key, Map<String, Object> v, long l, TimeUnit t) {
        try {
            lock.lock();
            Map<String, Object> map = (Map<String, Object>) this.redisTemplate.opsForValue().get(key);
            if (!CollectionUtils.isEmpty(map)) {
                v.forEach((k, vl) -> {
                    map.remove(k);
                });
            }
            this.redisTemplate.opsForValue().set(key, map, l, t);
        } finally {
            lock.unlock();
        }
    }


    private void releaseConnection() {
        if (Objects.isNull(this.redisTemplate)) {
            throw new RuntimeException("redisTemplate instance can not be null...unable to release connection");
        }
        RedisConnectionFactory redisConnectionFactory = this.redisTemplate.getConnectionFactory();
        if (!Objects.isNull(redisConnectionFactory)) {
            RedisConnectionUtils.unbindConnection(redisConnectionFactory);
        }
    }

}
